package com.katze.boot.plugins.shiro.framework;

import org.apache.shiro.authz.annotation.*;
import org.apache.shiro.authz.aop.AuthorizingAnnotationMethodInterceptor;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AopAllianceAnnotationsAuthorizingMethodInterceptor;
import org.springframework.aop.support.StaticMethodMatcherPointcutAdvisor;
import org.springframework.core.annotation.AnnotationUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 * 说  明：权限校验
 * 作  者：zf.zeng
 * 日  期：2020-04-04 (星期六)
 **/
public class AuthorizationAttributeSourceAdvisor extends StaticMethodMatcherPointcutAdvisor {

    private static final long serialVersionUID = 3260853243034963967L;
    private static final List<Class<? extends Annotation>> annotations = new ArrayList<>();
    static {
        annotations.add(RequiresUser.class);
        annotations.add(RequiresRoles.class);
        annotations.add(RequiresGuest.class);
        annotations.add(RequiresPermissions.class);
        annotations.add(RequiresAuthentication.class);
    }

    private SecurityManager securityManager = null;

    /**
     * Create a new AuthorizationAttributeSourceAdvisor.
     */
    public AuthorizationAttributeSourceAdvisor() {
        this.setAdvice(new AopAllianceAnnotationsAuthorizingMethodInterceptor());
    }

    public SecurityManager getSecurityManager() {
        return securityManager;
    }

    public void setSecurityManager(SecurityManager securityManager) {
        this.securityManager = securityManager;
    }

    /**
     * Returns <tt>true</tt> if the method or the class has any Shiro annotations, false otherwise.
     * The annotations inspected are:
     * <ul>
     * <li>{@link RequiresAuthentication RequiresAuthentication}</li>
     * <li>{@link RequiresUser RequiresUser}</li>
     * <li>{@link RequiresGuest RequiresGuest}</li>
     * <li>{@link RequiresRoles RequiresRoles}</li>
     * <li>{@link RequiresPermissions RequiresPermissions}</li>
     * </ul>
     *
     * @param method      the method to check for a Shiro annotation
     * @param targetClass the class potentially declaring Shiro annotations
     * @return <tt>true</tt> if the method has a Shiro annotation, false otherwise.
     * @see org.springframework.aop.MethodMatcher#matches(Method, Class)
     */
    public boolean matches(Method method, Class<?> targetClass) {
        Method m = method;

        if (this.isAuthzAnnotationPresent(method) ) {
            return true;
        }

        //The 'method' parameter could be from an interface that doesn't have the annotation.
        //Check to see if the implementation has it.
        try {
            m = targetClass.getMethod(m.getName(), m.getParameterTypes());
            return this.isAuthzAnnotationPresent(m) || this.isAuthzAnnotationPresent(targetClass);
        } catch (NoSuchMethodException ignored) {
            //default return value is false.  If we can't find the method, then obviously
            //there is no annotation, so just use the default return value.
        }
        return false;
    }

    private boolean isAuthzAnnotationPresent(Class<?> targetClazz) {
        for( Class<? extends Annotation> annClass : annotations ) {
            Annotation a = AnnotationUtils.findAnnotation(targetClazz, annClass);
            if ( a != null ) {
                return true;
            }
        }
        return false;
    }

    private boolean isAuthzAnnotationPresent(Method method) {
        for( Class<? extends Annotation> annClass : annotations ) {
            Annotation a = AnnotationUtils.findAnnotation(method, annClass);
            if ( a != null ) {
                return true;
            }
        }
        return false;
    }

    public void add(AuthorizingAnnotationMethodInterceptor annotationMethodInterceptor){
        Collection<AuthorizingAnnotationMethodInterceptor> list = ((AopAllianceAnnotationsAuthorizingMethodInterceptor)getAdvice()).getMethodInterceptors();
        list.add(annotationMethodInterceptor);
        annotations.add(annotationMethodInterceptor.getHandler().getAnnotationClass());
    }
}
